﻿-- Author      : Kurapica
-- Create Date : 9/6/2008

-- TreeView
--[[
		example tree

		Alpha
		Bravo
		  -Charlie
		  -Delta
			-Echo
		Foxtrot

		tree = {
			{
				["Value"] = "A",
				["Text"] = "Alpha",
                ["Menu"] = DropDownList1,
			},
			{
				["Value"] = "B",
				["Text"] = "Bravo",
                ["Menu"] = DropDownList2,
				["Childs"] = {
					{
						["Value"] = "C",
						["Text"] = "Charlie",
					},
					{
						["Value"] = "D",
						["Text"] = "Delta",
						["Childs"] = {
							{
								["Value"] = "E",
								["Text"] = "Echo",
                                ["Menu"] = DropDownList3,
							}
						}
					}
				}
			},
			{
				["Value"] = "F",
				["Text"] = "Foxtrot"
			},
		}
--]]
do
	-- Check Version
	local version = 1
	if not IGAS:RegisterFile("IGAS.GUI.Widget.TreeView", version) then
		return
	end

    local getmetatable = getmetatable
    local pairs = pairs
    local ipairs = ipairs
    local type = type
    local error = error
    local CreateFrame = IGAS.GUI.CreateFrame

	local _GUI = IGAS.GUI
	local _Widget = IGAS.GUI.Widget
    local _BaseFunc = IGAS.GUI.Base.Func
	local _PropertyType = IGAS.GUI.Base.PropertyType

    ------------------------------------------------------
	---------------------- TreeNode ----------------------
	------------------------------------------------------
    local TreeNode

    do
        local _WidgetName = "TreeNode"
        local _Base = "Frame"

        local nodeHeight = 18
        local nodeIndent = 16

        -- Scripts
		local function FixHeight(self)
			local height = nodeHeight

			if self:GetChild("TreeNode1") then
				if self.__NodeNum > 0 then
					for i = 1, self.__NodeNum do
						if self:GetChild("TreeNode"..i) and self:GetChild("TreeNode"..i).Visible then
							height = height + self:GetChild("TreeNode"..i).Height
						end
					end
				end
			end

			self.Height = height
		end

		local function AddNode(self, node)
			local root = self.__Root
            local value

			if node and type(node) == "table" and
				(node.Text and type(node.Text) == "string" and node.Text ~= "") and
				(node.Value and type(node.Value) == "string" and node.Value ~= "") then

				-- Value Check
				value = self.__Value.."\001"..node.Value
                if root.__Nodes[value] then
                    error("This node["..value.."] is exist", 2)
                end

				local frame = CreateFrame(TreeNode, nil, self)
				frame:SetNode(node)
                
                return frame
			end
		end

		local function RemoveNode(self, key)
			if key then
				if type(key) == "number" then
					if self:GetChild("TreeNode"..key) then
						self:GetChild("TreeNode"..key):Dispose()
					end
				elseif type(key) == "string" then
					if self.__Root.__Nodes[key] then
						self.__Root.__Nodes[key]:Dispose()
					end
				end
			end
		end

		local function SetNode(self, node)
			local root = self.__Root
			local value
			local index
			local idx, cht
			local childNode

			if node and type(node) == "table" and
				(node.Text and type(node.Text) == "string" and node.Text ~= "") and
				(node.Value and type(node.Value) == "string" and node.Value ~= "") then

				-- Text
				self:GetChild("TextBtn"):GetChild("Text").Text = node.Text

				-- Value
                value = ""
                if self.Parent.__Value and self.Parent.__Value ~= "" then
                    value = self.Parent.__Value.."\001"
                end
				value = value..node.Value
				if self.__Value ~= value then
					if self.__Value then
						root.__Nodes[self.__Value] = nil
					end
					self.__Value = value
					root.__Nodes[self.__Value] = self
				end

                -- Menu
                if node.Menu and type(node.Menu) == "table" and node.Menu["IsObjectType"] and node.Menu:IsObjectType("DropDownList") then
                    self.__DropDownList = node.Menu
                end

				-- Childs
				index = 0
				--- clear child's key
				if self:GetChild("TreeNode1") then
					if self.__NodeNum > 0 then
						for i = 1, self.__NodeNum do
							childNode = self:GetChild("TreeNode"..i)
							if childNode.__Value then
								root.__Nodes[childNode.__Value] = nil
								childNode.__Value = nil
							end
						end
					end
				end
				--- Set node's child
				if node.Childs and type(node.Childs) == "table" then
					idx, cht = next(node.Childs)
					while(idx) do
						if cht and type(cht) == "table" and
							(cht.Text and type(cht.Text) == "string" and cht.Text ~= "") and
							(cht.Value and type(cht.Value) == "string" and cht.Value ~= "") then

							index = index + 1

							if self:GetChild("TreeNode"..index) then
								self:GetChild("TreeNode"..index):SetNode(cht)
							else
								self:AddNode(cht)
							end
						end
						idx, cht = next(node.Childs, idx)
					end
				end
				--- remove useless child-node.
				if self.__NodeNum > index then
					for i = self.__NodeNum, index+1, -1 do
						self:RemoveNode(i)
					end
				end
			end
		end

		local function OnSizeChanged(self)
			FixHeight(self.Parent)
		end

		local function Select(self)
			local root = self.__Root
			local parent = self.Parent
			
			-- Toggle
			while parent and parent ~= root do
				parent.ToggleState = true
				parent = parent.Parent
			end

			-- Select
            if root.__SelectedItem ~= self then
				if root.__SelectedItem then
					root.__SelectedItem:GetChild("TextBtn"):UnlockHighlight()
				end
                self:GetChild("TextBtn"):LockHighlight()
                root.__SelectedItem = self
                root:Fire("OnNodeClk")
            end
		end
		
        local function OnClick(self, button)
            local root = self.Parent.__Root

            if button == "RightButton" then
                -- Open Menu
                if self.Parent.__DropDownList then
                    self.Parent.__DropDownList.Visible = false
                    self.Parent.__DropDownList.Parent = self.Parent
                    self.Parent.__DropDownList.Visible = true
                end
                return
            end

			Select(self.Parent)
        end

		local function ToggleBtn_OnClick(self)
			self.Parent.ToggleState = not self.Parent.ToggleState
		end

        -- ScriptType
        local _ScriptType = {
            ["OnToggle"] = true,
        }
		
        -- FuncProxy
        local _FuncProxy = {
			["GetText"] = function(self)
				return self:GetChild("TextBtn"):GetChild("Text").Text
			end,

			["GetValue"] = function(self)
                self.__Value = self.__Value or ""
                return strsplit("\001", self.__Value)
			end,

			["AddNode"] = function(self, node)
				return AddNode(self, node)
			end,
            
            ["GetNode"] = function(self, key)
                if key then
                    if type(key) == "number" then
                        if self:GetChild("TreeNode"..key) then
                            return self:GetChild("TreeNode"..key)
                        end
                    elseif type(key) == "string" then
                        if self.__Root.__Nodes[key] then
                            return self.__Root.__Nodes[key]
                        end
                    end
                end
            end,
            
			["IsSelected"] = function(self)
				return (self.__Root.__SelectedItem == self)
			end,
			
			["Select"] = function(self)
				Select(self)
			end,
			
			["RemoveNode"] = function(self, key)
				RemoveNode(self, key)
			end,

			["SetNode"] = function(self, node)
				SetNode(self, node)
			end,
			
            ["GetToggleState"] = function(self)
                return self.__ToggleState
            end,

			["SetToggleState"] = function(self, flag)
				flag = (flag and true) or false
				
                if self.__ToggleState == flag then
                    return
                end

				if flag == false then
					self.__ToggleState = false
					self:GetChild("ToggleBtn"):SetNormalTexture("Interface\\Buttons\\UI-PlusButton-UP")
					self:GetChild("ToggleBtn"):SetPushedTexture("Interface\\Buttons\\UI-PlusButton-DOWN")

					if self.__NodeNum > 0 then
						for i = self.__NodeNum,1,-1 do
							self:GetChild("TreeNode"..i):Hide()
						end
					end
				else
					self.__ToggleState = true
					self:GetChild("ToggleBtn"):SetNormalTexture("Interface\\Buttons\\UI-MinusButton-UP")
					self:GetChild("ToggleBtn"):SetPushedTexture("Interface\\Buttons\\UI-MinusButton-DOWN")

					if self.__NodeNum > 0 then
						local frame
						for i = 1, self.__NodeNum do
							frame = self:GetChild("TreeNode"..i)

							frame:Show()

							-- SetPoint
                            if i == 1 then
                                frame:SetPoint("TOPRIGHT", self, "TOPRIGHT", 0, - nodeHeight)
                                frame:SetPoint("TOPLEFT", self, "TOPLEFT", nodeIndent, - nodeHeight)
                            else
                                frame:SetPoint("TOPRIGHT", self:GetChild("TreeNode"..(i-1)), "BOTTOMRIGHT", 0, 0)
                                frame:SetPoint("TOPLEFT", self:GetChild("TreeNode"..(i-1)), "BOTTOMLEFT", 0, 0)
                            end
						end
					end
				end

				FixHeight(self)
                
                self:Fire("OnToggle")
			end,

			-- Dispose, release resource
	        ["Dispose"] = function(self)
				local index = self.ID
                local parent = self.Parent

                self:SetToggleState(false)
                
                -- Dispose child node first
                if self.__NodeNum > 0 then
                    for i = self.__NodeNum, 1, -1 do
                        self:GetChild("TreeNode"..i):Dispose()
                    end
                end

                self.OnSizeChanged = nil
				self:StackScript("OnSizeChanged", nil, true)

				if parent.ToggleState or self.__Root == parent then
	                if parent:GetChild("TreeNode"..(index+1)) then
                        if index == 1 then
                            if self.__Root == parent then
                                parent:GetChild("TreeNode"..(index+1)):SetPoint("TOPRIGHT", parent, "TOPRIGHT", 0, -6)
                                parent:GetChild("TreeNode"..(index+1)):SetPoint("TOPLEFT", parent, "TOPLEFT", 0, -6)
                            else
                                parent:GetChild("TreeNode"..(index+1)):SetPoint("TOPRIGHT", parent, "TOPRIGHT", 0, - nodeHeight)
                                parent:GetChild("TreeNode"..(index+1)):SetPoint("TOPLEFT", parent, "TOPLEFT", nodeIndent, - nodeHeight)
                            end
                        else
                            parent:GetChild("TreeNode"..(index+1)):SetPoint("TOPRIGHT", parent:GetChild("TreeNode"..(index-1)), "BOTTOMRIGHT", 0, 0)
                            parent:GetChild("TreeNode"..(index+1)):SetPoint("TOPLEFT", parent:GetChild("TreeNode"..(index-1)), "BOTTOMLEFT", 0, 0)
                        end
	                end
				end
				
                self:ClearAllPoints()

                self:Hide()

                parent:RemoveChild(self)

				if parent.__NodeNum > index then
	                for i = index + 1, parent.__NodeNum do
						parent:GetChild("TreeNode"..i).ID = i - 1
	                    parent:GetChild("TreeNode"..i).Name = "TreeNode"..(i - 1)
	                end
				end

				parent.__NodeNum = parent.__NodeNum - 1

                FixHeight(parent)

				if parent.__NodeNum == 0 then
                    if parent:GetChild("ToggleBtn") then
					    parent:GetChild("ToggleBtn").Visible = false
                    end
				end

				if self.__Value then
					self.__Root.__Nodes[self.__Value] = nil
					self.__Value = nil
				end
                
                if self.__Root.__SelectedItem == self then
                    self.__Root.__SelectedItem = nil
                end

	            -- Call super's dispose
				self:CallMethod(_Widget[_Base], "Dispose")
	        end,
        }

        -- Property
        local _Property = {
			-- Text
			["Text"] = {
				["Get"] = function(self)
					return self:GetText()
				end,

				["Type"] = _PropertyType.String,
			},
			-- ToggleState
			["ToggleState"] = {
				["Set"] = function(self, flag)
					self:SetToggleState(flag)
				end,

				["Get"] = function(self)
					return self:GetToggleState()
				end,

				["Type"] = _PropertyType.Boolean,
			},
			-- Menu
            ["Menu"] = {
                ["Set"] = function(self, list)
                    self.__DropDownList = list
                end,

                ["Get"] = function(self)
                    return self.__DropDownList
                end,

                ["Type"] = _PropertyType.Object,
            },
			-- CanToggle
			["CanToggle"] = {
				["Get"] = function(self)
					if self.__NodeNum > 0 then
						return true
					else
						return false
					end
				end,

				["Type"] = _PropertyType.Boolean,
			},
			-- ChildNodeNum			
			["ChildNodeNum"] = {
				["Get"] = function(self)
					return self.__NodeNum or 0
				end,

				["Type"] = _PropertyType.Number,
			},
        }

        -- New
        local _New = function(parent)
            local index = parent.__NodeNum or 0
            index = index + 1

			-- Base Frame
            local frame = CreateFrame("Frame", nil, parent)
			
			-- Init
			frame.__NodeNum = 0
			frame.ID = index
			frame.__ToggleState = false
            frame.__Root = parent.__Root or parent
			if frame.__Root ~= parent then
				frame.Visible = parent.ToggleState
			else
				frame.Visible = true
			end
            if parent:GetChild("ToggleBtn") then
                parent:GetChild("ToggleBtn").Visible = true
            end

			frame.Height = nodeHeight
			if frame.Visible then
				parent.Height = parent.Height + nodeHeight
				if index == 1 then
					if frame.__Root == parent then
						frame:SetPoint("TOPRIGHT", parent, "TOPRIGHT", 0, -6)
						frame:SetPoint("TOPLEFT", parent, "TOPLEFT", 0, -6)
					else
						frame:SetPoint("TOPRIGHT", parent, "TOPRIGHT", 0, - nodeHeight)
						frame:SetPoint("TOPLEFT", parent, "TOPLEFT", nodeIndent, - nodeHeight)
					end
				else
					frame:SetPoint("TOPRIGHT", parent:GetChild("TreeNode"..(index-1)), "BOTTOMRIGHT", 0, 0)
					frame:SetPoint("TOPLEFT", parent:GetChild("TreeNode"..(index-1)), "BOTTOMLEFT", 0, 0)
	            end
			end
			parent.__NodeNum = index

			-- Toggle Button
            local toggleBtn = CreateFrame("Button","ToggleBtn",frame)
			toggleBtn.Visible = false
            toggleBtn.Width = 14
            toggleBtn.Height = 14
            toggleBtn:SetPoint("TOPLEFT", frame, "TOPLEFT", 4, - 1)

			toggleBtn:SetNormalTexture("Interface\\Buttons\\UI-PlusButton-UP")
			toggleBtn:SetPushedTexture("Interface\\Buttons\\UI-PlusButton-DOWN")
            toggleBtn:SetHighlightTexture("Interface\\Buttons\\UI-PlusButton-Hilight")
            local t = toggleBtn:GetHighlightTexture()
            t:SetBlendMode("ADD")

			-- Text Button
			local textBtn = CreateFrame("Button", "TextBtn", frame)
			textBtn.Height = nodeHeight
			textBtn:SetPoint("RIGHT", frame, "RIGHT", 0, 0)
			textBtn:SetPoint("TOP", frame, "TOP", 0, 0)
			textBtn:SetPoint("LEFT", frame, "LEFT", 14, 0)
			textBtn:RegisterForClicks("AnyUp")

            t = CreateFrame("FontString", "Text", textBtn, "OVERLAY")
			t:SetFontObject(GameFontNormal)
			t:SetJustifyH("LEFT")
            t:SetAllPoints(textBtn)

			textBtn:SetHighlightTexture("Interface\\QuestFrame\\UI-QuestLogTitleHighlight")
            t = textBtn:GetHighlightTexture()
            t:SetBlendMode("ADD")
			t:SetPoint("TOPLEFT", textBtn, "TOPLEFT", 0, 1)
			t:SetPoint("BOTTOMRIGHT", textBtn, "BOTTOMRIGHT", 0, 1)

			-- scripts
			toggleBtn.OnClick = ToggleBtn_OnClick
			textBtn.OnClick = OnClick
			textBtn.OnDoubleClick = OnDoubleClick
			frame:StackScript("OnSizeChanged", OnSizeChanged)

            return frame
        end

        local _WidgetInfo = {
            ["WidgetName"] = _WidgetName,
            ["Base"] = _Base,
            ["FuncProxy"] = _FuncProxy,
            ["Property"] = _Property,
            ["ScriptType"] = _ScriptType,
            ["New"] = _New,
        }

        TreeNode = _GUI.NewWidget(_WidgetInfo)
    end

    ------------------------------------------------------
	--------------------- TreeView ----------------------
	------------------------------------------------------
	local _WidgetName = "TreeView"
    local _Base = "ScrollForm"

    -- Scripts
	local function OnNodeClk(self)
		self.Back:Fire("OnNodeSelected", self.Back:GetValue())
	end

	-- ScriptType
	local _ScriptType = {
        ["OnNodeSelected"] = true,
	}
	
	-- FuncProxy
	local _FuncProxy = {
        -- Dispose, release resource
        ["Dispose"] = function(self)			
			local container = self.Container
            
            if container.__NodeNum and container.__NodeNum > index then
                for i = container.__NodeNum, index+1, -1 do
                    container:GetChild("TreeNode"..i):Dispose()
                end
            end
            container.__SelectedItem = nil

            -- Call super's dispose
			self:CallMethod(_Widget[_Base], "Dispose")
        end,
            
		["GetNodeByValue"] = function(self, ...)
			local value, e
			local container = self.Container

			if select('#', ...) == 0 then
	            return nil
	        end

			value = ""

	        for i = 1, select('#', ...) do
	            e = select(i, ...)
	            if e and type(e) == "string" then
                    if value == "" then
                        value = e
                    else
	                    value = value.."\001"..e
                    end
	            end
	        end

			if value ~= "" and self.Container.__Nodes[value] then
				return self.Container.__Nodes[value]
			end
		end,

		["RemoveNodeByValue"] = function(self, ...)
			local value, e

			if select('#', ...) == 0 then
	            return nil
	        end

			value = ""
	        for i = 1, select('#', ...) do
	            e = select(i, ...)
	            if e and type(e) == "string" then
	                if value == "" then
                        value = e
                    else
	                    value = value.."\001"..e
                    end
	            end
	        end

			if value ~= "" and self.Container.__Nodes[value] then
				self.Container.__Nodes[value]:Dispose()
			end
		end,

		["SelectNodeByValue"] = function(self, ...)
			local value, e
			local container = self.Container

			if select('#', ...) == 0 then
	            return nil
	        end

			value = ""
			-- Toggle
	        for i = 1, select('#', ...) do
	            e = select(i, ...)
	            if e and type(e) == "string" then
	                if value == "" then
                        value = e
                    else
	                    value = value.."\001"..e
                    end

					if value ~= "" and container.__Nodes[value] then
						if container.__Nodes[value]:GetChild("TreeNode1") then
							container.__Nodes[value].ToggleState = true
						end
					end
	            end
	        end

			-- Select
			if value ~= "" and container.__Nodes[value] then				
				container.__Nodes[value]:Select()
			end
		end,
        
        ["GetSelectNode"] = function(self)
            if self.Container.__SelectedItem then
				return self.Container.__SelectedItem
			end
        end,

		["GetValue"] = function(self)
			if self.Container.__SelectedItem then
				return self.Container.__SelectedItem:GetValue()
			end
		end,

        ["AddNode"] = function(self, node)
            local container = self.Container			
            local value

			if node and type(node) == "table" and
				(node.Text and type(node.Text) == "string" and node.Text ~= "") and
				(node.Value and type(node.Value) == "string" and node.Value ~= "") then

				-- Value Check
				value = ""..node.Value
                if container.__Nodes[value] then
                    error("This node["..value.."] is exist", 2)
                end

				local frame = CreateFrame(TreeNode, nil, container)
				frame:SetNode(node)
				return frame
			end
        end,
        ["GetNode"] = function(self, key)
            local container = self.Container
            if key and type(key) == "number" then
                if container:GetChild("TreeNode"..key) then
                    return container:GetChild("TreeNode"..key)
                end
            end
        end,
		["SetTree"] = function(self, tree)
			local container = self.Container
			local index = 0
			local idx, cht
			local frame
            
            if not tree then
                if container.__NodeNum and container.__NodeNum > index then
                    for i = container.__NodeNum, index+1, -1 do
                        container:GetChild("TreeNode"..i):Dispose()
                    end
                end
                container.__SelectedItem = nil
                return
            end
            
            idx, cht = next(tree)
			while(idx) do
				if cht and type(cht) == "table" and
					(cht.Text and type(cht.Text) == "string" and cht.Text ~= "") and
					(cht.Value and type(cht.Value) == "string" and cht.Value ~= "") then

					index = index + 1

					if container:GetChild("TreeNode"..index) then
						container:GetChild("TreeNode"..index):SetNode(cht)
					else
						node = CreateFrame(TreeNode, nil, container)
						node:SetNode(cht)
					end
				end
				idx, cht = next(tree, idx)
			end
			if container.__NodeNum and container.__NodeNum > index then
				for i = container.__NodeNum, index+1, -1 do
					container:GetChild("TreeNode"..i):Dispose()
				end
			end

            -- First time, the text don't show, amazing....
            --self:GetChild("ScrollFrame"):GetChild("Bar").Value = 10
            --self:GetChild("ScrollFrame"):GetChild("Bar").Value = 0
		end,

        -- Dispose, release resource
        ["Dispose"] = function(self)
            self:SetTree({})

            -- Call super's dispose
			self:CallMethod(_Widget[_Base], "Dispose")
        end,
    }

	-- Property
	local _Property = {
        ["ChildNodeNum"] = {
			["Get"] = function(self)  
				return self.Container.__NodeNum or 0
			end,

			["Type"] = _PropertyType.Number,
		},
	}

    -- New
    local _New = function(parent)
		local frame = CreateFrame("ScrollForm",nil,parent)
        frame.ValueStep = 10
		local container = frame.Container

		container.Height = 30
		container.__Nodes = {}

		container.OnNodeClk = OnNodeClk

        return frame
    end

    local _WidgetInfo = {
        ["WidgetName"] = _WidgetName,
        ["Base"] = _Base,
        ["FuncProxy"] = _FuncProxy,
        ["Property"] = _Property,
        ["ScriptType"] = _ScriptType,
        ["New"] = _New,
    }

    _Widget[_WidgetName] = _GUI.NewWidget(_WidgetInfo)
end